#define DDCLLUA_CORE

#include "ddclmd5.h"
#include "lua-ddcl.h"

static int
l_encode (lua_State * L){
    size_t len;
    const char * str = luaL_checklstring(L, 1, &len);
    ddcl_Md5Ctx ctx;
    ddcl_init_md5(&ctx);
    ddcl_update_md5(&ctx, str, len);
    dduint8 digest[DDCL_MD5_DIGEST_SIZE];
    ddcl_final_md5(&ctx, digest);
    lua_pushlstring(L, digest, DDCL_MD5_DIGEST_SIZE);
    return 1;
}

static int
l_tostring (lua_State * L){
    size_t len;
    const dduint8 * digest = (const dduint8*)luaL_checklstring(L, 1, &len);
    if(len != DDCL_MD5_DIGEST_SIZE){
        return luaL_error(L, "Invalid digest string");
    }
    dduint8 result[DDCL_MD5_STRING_SIZE];
    ddcl_tostring_md5(digest, result);
    lua_pushstring(L, result);
    return 1;
}

static int
l_ctx (lua_State * L){
    ddcl_Md5Ctx * ctx = (ddcl_Md5Ctx *)lua_newuserdata(L, sizeof(ddcl_Md5Ctx));
    ddcl_init_md5(ctx);
    return 1;
}

static int
l_update (lua_State * L){
    ddcl_Md5Ctx * ctx = (ddcl_Md5Ctx *)lua_touserdata(L, 1);
    if(!ctx){
        return luaL_error(L, "userdata expect, got NULL");
    }
    size_t len;
    const char * str = luaL_checklstring(L, 2, &len);
    ddcl_update_md5(ctx, str, len);
    return 0;
}

static int
l_final (lua_State * L){
    ddcl_Md5Ctx * ctx = (ddcl_Md5Ctx *)lua_touserdata(L, 1);
    if(!ctx){
        return luaL_error(L, "userdata expect, got NULL");
    }
    dduint8 digest[DDCL_MD5_DIGEST_SIZE];
    dduint8 result[DDCL_MD5_STRING_SIZE];
    ddcl_final_md5(ctx, digest);
    ddcl_tostring_md5(digest, result);
    lua_pushlstring(L, digest, DDCL_MD5_DIGEST_SIZE);
    lua_pushlstring(L, result, DDCL_MD5_STRING_SIZE);
    return 2;
}


static luaL_Reg _REG[] = {
    { NULL, NULL },
};

DDCLLUA int
luaopen_ddcl_md5 (lua_State * L){
    luaL_checkversion(L);
    luaL_newlib(L, _REG);
    luapush_ddcl_md5(L, -1);
    return 1;
}

DDCLLUA int
luapush_ddcl_md5 (lua_State * L, int i){
    lua_checkstack(L, 3);
    lua_pushvalue(L, i);
    DDLUA_PUSHFUNC(L, "encode", l_encode);
    DDLUA_PUSHFUNC(L, "tostring", l_tostring);
    DDLUA_PUSHFUNC(L, "ctx", l_ctx);
    DDLUA_PUSHFUNC(L, "update", l_update);
    DDLUA_PUSHFUNC(L, "final", l_final);
    lua_pop(L, 1);
    return 0;
}
